/**
 * \file nlb-driver.c
 * 
 * Arquivo main do L2LB.
 * Este arquivo eh responsavel por registrar/desregistrar o modulo no kernel do linux
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/if_ether.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/vmalloc.h>
#include <linux/kthread.h>
#include <linux/delay.h> 
#include <linux/net.h>
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#define MODULE_NAME "L2LB"
#endif


#include "utils.h"
#include "cluster.h"
#include "hash-afinidades.h"
#include "socket.h"
#include "heartbeat.h"
#include "thread.h"
#include "interface.h"
#include "netfilter.h"

#ifndef DOXYGEN_SHOULD_SKIP_THIS
MODULE_DESCRIPTION("Layer 2 Load Balancing Kernel Module");
MODULE_AUTHOR("Daniel Gomes Silveira (dan_gs@im.ufba.br)");
MODULE_LICENSE("$LICENSE$");
#endif

/**
 * Interface de rede do cluster que sera gerenciada pelo L2LB
 */
static char dev[9];

/**
 * IP virtual do cluster
 */
static char ip[17];

#ifndef DOXYGEN_SHOULD_SKIP_THIS
module_param_string(dev, dev, sizeof(dev), 0);
module_param_string(ip, ip, sizeof(ip), 0);
#endif

/**
 * Wrapper do metodo dev.hard_start_xmit_old() implementado pelo driver rede responsavel
 * por enviar o pacote de rede.
 * 
 * Este metodo remonta o cabecalho do pacote na camada de enlace para os pacotes 
 * que estao sendo enviados pela interface de rede do cluster.
 * 
 * No L2LB, o cabecalho do pacote da resposta na camada de enlace tem como remetente
 * outra interface de rede, que nao a interface de rede do cluster, por onde a requisicao
 * foi recebida. Isto eh necessario para impedir que os swicths de rede apredam uma rota 
 * para o IP virtual do cluster, uma vez que o IP virtual do cluster eh compartilhado 
 * por varias maquinas na LAN. Desta forma todos os servidores do cluster sempre receberao
 * todas as mensagem enderecadas para o seu IP virtual.
 * 
 * \param skb Pacote 
 * \param dev Interface de rede
 */
int	dev_cluster_hard_start_xmit_new (struct sk_buff *skb, struct net_device *dev)
{
	struct ethhdr *eth;
	
	eth = (struct ethhdr *)skb->data;
	 
	memcpy(&eth->h_source, &my_cluster.mac_response, ETH_ALEN);
	
	return my_cluster.hard_start_xmit_old(skb, dev);
}

/**
 * Incializa o modulo do kernel.
 * Esta funcao eh responsavel por:
 *  - Inicializar o cluster. init_cluster()
 *  - Inicializar a interface com o modulo atraves do sistema de arquivo \\proc. init_proc_interface()
 *  - Inicializar a tabela de afinidades. hash-afinidades.h
 *  - Inicializar um thread para comunicacao entre os servidores do cluster. thread.h
 *  - Inicializar os HOOKS do Netfilter. nlb_netfilter_init()
 */
static int nlb_init_module(void)
{		
	//Inicializa a estruturas do cluster
	if(init_cluster(dev, ip, &dev_cluster_hard_start_xmit_new) < 0)
		return -1;
	
	//Inicializa a tabela HASH de afinidades
	hash_init(); 
		
	//Inicializa a interface com o modulo do kernel atraver de /proc/nlb
	if(init_proc_interface() < 0)
		return -1;
	
	printk_mac_addr((u8 *)&my_cluster.mac_response);
		
	//Inicializa a Thread que ira gerenciar a comunicacao entre os servidores do cluster
	//via socket em uma VLAN privada.
	//nlb_thread_init();
	
	//Registra os HOOKS do Netfilter
	if(nlb_netfilter_init() < 0) 
		return -1;
	
	printk( KERN_ALERT MODULE_NAME ": Module nlb init! 2\n" );
	return 0;
}

/**
 * Desregistra o modulo
 */
static void nlb_exit_module(void)
{
	//Remove o hook do Netfilter
	nlb_netfilter_exit();
	
	//Remove a interface /proc/nlb
	exit_proc_interface();

	//Remove o servidores do cluster
	nlb_cluster_exit();

	//Libera a tabela de afinidades
	hash_exit();
	
	//Finaliza a Thread do socket
	//nlb_thread_exit();
	
	printk( KERN_ALERT MODULE_NAME ": Module nlb exit\n" );
}

#ifndef DOXYGEN_SHOULD_SKIP_THIS

/**
 * Linka com as funcao de registrar/desregistrar o modulo
 */
module_init(nlb_init_module);
module_exit(nlb_exit_module);
#endif

